CPS222 Lecture: Pointers and References in C++ Last revised 1/31/15 Objectives: 1. To introduce C++ pointers 2. To introduce dynamic storage allocation/deallocation in C++. 3. To introduce C++ references Materials: 1. C++/Java comparison handout I. Pointers - -------- A. Some notes on computer memory and variables 1. On almost all computers, memory is structured as a sequence of bytes, each of which has an address. Example: A computer with 1 GB of mememory would a memory with addresses ranging from 0 .. 1,073,741,823. 2. If a value requires more than one byte to store (as almost all do), it is given the appropriate number of successive memory locations. Example: If an int requires 4 bytes, then it might be allocated - say - bytes 1000, 1001, 1002, 1003 The first of these is typically regarded as the address of the item - e.g. the int referred to above would have address 1000 3. A variable is a symbolic name for a location in memory - so we can speak of the address of a variable. Storage is allocated (and this location is bound) in one of three ways, depending on how the variable is declared. a. A static variable is permanently allocated the appropriate amount of storage. b. For a local variable (or parameter), storage is allocated when the function in which it is declared is entered, and deallocated when it is terminated. The binding between the variable name and the address is in effect only while the function is active. Note: In the case of recursive functions, this may mean that the same name refers to several different memory locations - but there is no ambiguity because only one invocation is current at any one time. c. For a dynamic variable, storage is allocated by new and deallocated by delete. 4. In both C and C++, the operator & is the "address of" operator, and can be applied to any variable to give the address in memory that variable is a name for Example: If int foo; results in the variable foo being associated with locations 1000-1003 in memory, then & foo is 1000. B. Pointers in C++ - For examples, we will assume the following class declaration class Demo { int _i; double _d; }; 1. Pointers are found in many progamming languages. They entered C++ from C. 2. A pointer variable (or field of a class) is declared in C/C++ by a declaration of the form * e.g. int * p; // p is a pointer to an int Demo * p; // p is a pointer to a Demo object 3. A pointer variable is a symbolic name for a location in memory that holds the address in memory of _another_ item. Example: Demo * p = new Demo() might result in something like this ------ ------ p | o|--> | _i | ------ | _d | | | ------ (A pointer variable can refer to no object, in which case it has the value 0 - symbolically NULL) 4. A pointer variable can be used to refer to itself or the "pointee" - if there is one. a. A usage of the variable name by itself refers to the pointER: Pointers are subject only to the following operations: assignment, comparison for equality (==, != - same object), increment/decrement ++, -- (Done in steps of declared pointEE type size) Example: Demo * p, * q; ... q = p; // makes q refer to the same object p does if (p == NULL) // tests whether there is actually an object that p points to b. It can be dereferenced to refer to the pointEE: Example: In the above, *p is a Demo object, (*p)._i is the _i field of this object, etc. c. In C++, p -> _i and (*p)._i are equivalent, but the former is preferred for readability C. Go over section 11 in handout II. Storage Allocation in C++ ------- ---------- -- --- Go over section 12 in handout III. References --- ---------- A. References in C++ 1. References are a new feature in C++ - not found in C 2. A reference variable (or field of a class) is declared in C/C++ by a declaration of the form & e.g. int & p; // p is a reference to an int Demo & p; // p is a reference to a Demo object 3. A reference variable is implemented in exactly the same way as a pointer variable is (i.e. it is the symbolic name of a location in memory that holds the address in memory of another item). However, while the implementation is the same, the way the compiler handles a reference variable is different. a. A reference is immutable. When it is created, it must be assigned what it is to refer to, and it cannot thereafter be changed. b. All uses of a reference variable affect the referEE - not the reference. (There is therefore no need for an explicit dereferencing operation like * or -> with pointers) 4. In fact, a reference is an alias - another name for the same thing as the item it refers to. Anything use of the reference has exactly the same meaning as doing the same thing with the thing it refers to. Examples: If Clark Kent flies to London, Superman is in England. If Superman is exposed to kryptonite, Clark Kent dies 5. One of the key uses for references in C++ is for something known as "call by reference". a. Consider the matter of passing parameters to a function. The conventional way of handling this is something known as "call by value": the function is given a COPY of the actual parameter. b. A strength of this is that it allows the parameter to be a variable, a constant, or even an expression. c. There are two weaknesses, however i. Any changes made by the function are not visible to the caller, since they are made to a copy, not to the original. (Contrast making a photocopy of a book from the library as opposed to checking out the actual book - what happens in each case if you write on what you have?) ii. If the actual parameter is large, there is a lot of overhead needed to copy it. iii. For these reasons, one sometimes sees C functions in which a pointer is passed, rather than an actual value - Example: void increment(int n) { n ++; } ... int i = 3; increment(i); cout << i << endl; // writes 3 vs void increment(int * n) { (*n) ++; } ... int i = 3; increment(& i); cout << i << endl; // writes 4 The former writes 3, but the latter writes 4. Why? - Another example: void something(LargeClass c) { fields of c referred to by c. } ... LargeClass x; something(x); // Copies entire LargeClass object vs void something(LargeClass * c) { fields of c referred to using (*c). or c -> } ... LargeClass x; something(& x); // Copies only an address iv. With a C++ reference, these could be written as void increment(int & n) { n ++; } ... int i = 3; increment(i); cout << i << endl; // writes 4 - presumably as desired void something(LargeClass & c) { fields of c referred to by c. } ... LargeClass x; something(x); // Copies only an address (In both cases, same semantics as with a pointer, but much cleaner syntax. In some cases (e.g. operator overloading) pointer syntax becomes unusable.) B. Go over section 13 in handout C. Comparison to Java 1. Java has what it calls references, which are like both pointers and references in C++ in different ways. But note that whether a variable is a reference is solely determined by its type (so called "value types" and "reference types"). One cannot have a reference to an int or a variable of class type that is not a reference. 2. A Java reference acts like a C++ pointer for purposes of assignment and comparison, but like a C++ reference for all other purposes!